/*
 * dlmd.c
 *
 */

#include "dlmd.h"
#include "libdlm.h"
#include "log.h"
#include "request.h"
#include "lock.h"

#include <stdio.h>
#include <stdlib.h>
#include <paths.h>
#include <string.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <errno.h>

int opt_debug_level;
int opt_foreground;
char *opt_working_directory;
int opt_log_to_syslog;
char *opt_input_fifo_name;
char *opt_log_syslog_name;
char *opt_log_file;

static int input;		// input fifo descriptor

static void help(void);
static void dlmd_load_default_opts(void);
void dlmd_request_handler(struct request *);

int main(int argc, char **argv)
{
	/* signal handling */

	/* options handling */
	dlmd_load_default_opts();
	int opt;
	while ((opt = getopt(argc, argv, "fd:Vl:w:")) != -1)
	switch (opt) {
	case 'f':
		opt_foreground = 1;
		break;
	case 'd':
		opt_debug_level = atoi(optarg);
		opt_foreground = 1;
		break;
	case 'l':
		opt_log_file = optarg;
		break;
	case 'w':
		opt_working_directory = optarg;
		break;
	case 'V':
        printf("%s-%s", DLMD_NAME, DLMD_VERSION);
		printf("\n");
		exit(0);
	case 'h':
	case '?':
	default:
		help();
	}
	/* initializing log utility */
	dlmd_log_init();
	/* initializing communication */
	if (chdir(opt_working_directory)) {
		err("changing directory error: %s", strerror(errno));
		dlmd_exit(1);
	}
	if (mkfifo(opt_input_fifo_name, S_IFIFO | S_IRWXU | S_IRWXG | S_IRWXO))
		oops("creating fifo error: %s", strerror(errno));
	if (!opt_foreground) {
		debug("going to background");
		int fd = open(_PATH_TTY, O_RDWR | O_NOCTTY);
		if (fd >= 0) {
			(void) ioctl(fd, TIOCNOTTY, NULL);
			close(fd);
		} else
			err("cannot go to background");
	} else
		debug("staying in foreground");
	/* initializing timer */

	/* main */
	byte_t buff[DLMD_BUFF_SIZE];
	byte_t *ptr;
	ptr = buff;
	int bytes;
    //int res;
	debug2("opening fifo `%s'", opt_input_fifo_name);
	info("listening...");
	if ((input = open(opt_input_fifo_name, 0)) == -1) {
		err("cannot open input fifo: %s", strerror(errno));
		dlmd_exit(1);
	}
	while(1) {
		bytes = read(input, buff, sizeof(buff)-1);
		if (bytes == 0) {
			debug2("no writers to fifo, reopening...");
			close(input);
            if ((input = open(opt_input_fifo_name, O_RDONLY)) == -1) {
				err("reopening fifo failed: %s", strerror(errno));
				dlmd_exit(1);
			}
			continue;
		}
		debug2("read request/s, lenght: %d bytes", bytes);
		struct request *msg_ptr;
        msg_ptr = (struct request*) buff;
		struct request *new_msg_ptr;
		while (bytes > 0) {
            if ((bytes - msg_ptr->size) < 0)
				oops("bad request size");
			new_msg_ptr = (struct request*) malloc(msg_ptr->size);
			memcpy(new_msg_ptr, buff, msg_ptr->size);
			dlmd_request_handler(new_msg_ptr);
			bytes -= msg_ptr->size;
            //TODO: zmiana
			msg_ptr += msg_ptr->size;
		}
	}
	return 0;
}

void dlmd_request_handler(struct request *msg)
{
	switch (msg->type) {
	case DLMD_REQ_REGISTER:
		struct process *pr = lock_ck_process(msg->pid);
		struct stat st;
		int r = stat(msg->path, &st);
		if (r != 0) {
			dlmd_reply_ne(pr->fd);
			return;
		}
		struct resource res = lock_ck_resource_stat(&st);
		if (res_id == NULL)
			res = lock_add_resource(&st);
		dlmd_reply_ok(fd);
		break;
	case DLMD_REQ_TRYLOCK:

		break;
	case DLMD_REQ_LOCK:

		break;
	case DLMD_REQ_UNLOCK:

		break;
	}
	/*
	// count offsets
	int len;
	int off_pid = sizeof(byte_t);
	int off_filename = off_pid + sizeof(pid_t);
	int off_resource_id = off_filename;
	int off_lock_type = off_resource_id + sizeof(resource_id_t);
	int off_timeout = off_lock_type + sizeof(lock_type_t);
	char *name_ptr;
	pid_t p;
	switch (*(char*)buff) {
	case DLMD_REQ_REGISTER:
		name_ptr = (char*)(buff + off_filename);
		len = off_filename + strlen(name_ptr);
		char *name = (char*) malloc(sizeof(name_ptr)+1);
		//if ((p = fork()) == 0)
			dlmd_register(*(pid_t*)(buff + off_pid), name_ptr);
		break;
	case DLMD_REQ_LOCK:
		len = off_timeout + sizeof(timeout_t);
		if (bytes < len)
			return 0;
		//if ((p = fork()) == 0)
			dlmd_lock(	*(pid_t*)(buff + off_pid), \
						*(resource_id_t*)(buff + off_resource_id), \
						*(lock_type_t*)(buff + off_lock_type), \
						*(timeout_t*)(buff + off_timeout) );
		break;
	case DLMD_REQ_TRYLOCK:
		 len = off_lock_type + sizeof(lock_type_t);
		if (bytes < len)
			 return 0;
		//if ((p = fork()) == 0)
			dlmd_trylock(	*(pid_t*)(buff + off_pid), \
							*(resource_id_t*)(buff + off_resource_id), \
							*(lock_type_t*)(buff + off_lock_type) );
		break;
	case DLMD_REQ_UNLOCK:
		len = off_resource_id + sizeof(resource_id_t);
		if (bytes < len)
			return 0;
		//if ((p = fork()) == 0)
			dlmd_unlock(	*(pid_t*)(buff + off_pid), \
							*(resource_id_t*)(buff + off_resource_id) );
		break;
	default:
		return 0;
	}*/
	//if (p > 0)
	//	debug2("forked, new pid is %d", p);
	//else
	//	err("fork failed, cannot handle request, error: %s", strerror(errno));
	//return bytes - len;
}
/* load defaults options */
static void dlmd_load_default_opts(void)
{
	opt_debug_level = DLMD_LOG_INFO;
	opt_foreground = 0;
    opt_input_fifo_name = DLMD_INPUT_PIPE_NAME;
	opt_log_to_syslog = 0;
    opt_log_syslog_name = DLMD_NAME;
	opt_working_directory = "/var/run/dlmd";
	opt_log_file = "/var/log/dlmd.log";
}
/* exit program */
void dlmd_exit(int code)
{
	if (input > 0) {
		close(input);
		if (unlink(opt_input_fifo_name))
			err("cannot delete input fifo");
	}
	die("code: %d", code);
	exit(code);
}
/* display help message */
static void help(void)
{
	printf("usage: dlmd [-df]\n");
	printf("OPTIONS:\n");
	printf("	-V	print version and exit\n");
	printf("	-d	debug mode.\n");
	printf("	-f	stay in foreground.\n");
	printf("	\n");
	exit(0);
}
